package com.lg.patter.behavior.template.jdbc;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 使用模板方法模式 封装jdbc
 * <p>
 * 优点：
 * <p>
 * 1、 利用模板方法将相同处理逻编的代码放到抽象父类中 ， 可以提高代码的复用性。
 * <p>
 * 2、 将不同的代码不同的子类中 ， 通过对子类的扩展墙加新的行为 ， 提高代码的扩展性。
 * <p>
 * 3、 把不变的行为写在父类上 ， 去除子类的重复代码 ， 提供了一个很好的代码复用平台 ， 符合开闭原则。
 * <p>
 * 缺点：
 * <p>
 * 1、 类数目的培加 ， 每一个抽象类都需要一个子类来实现 ， 这样导致类的个数谧加。
 * <p>
 * 2、 类数呈的培加 ， 间接地墙加了系统实现的复杂度。
 * <p>
 * 3、 继承关系自身缺点 ， 如果父类添加新的抽象方法 ， 所有子类都要改—遍。
 *
 * @author by Mr. Li 2020/6/16 13:32
 */
public abstract class JdbcTemplate {
    private DataSource dataSource;

    public JdbcTemplate(DataSource dataSource) {
        this.dataSource = dataSource;
    }


    public final List<?> executeQuery(String sql, RowMapper<?> rowMapper, Object[] values) {
        try ( // 1. 获取连接
              Connection connection = this.getConnection();
              // 2. 创建语句集
              PreparedStatement pstm = this.createPrepareStatement(connection, sql);
              // 3. 执行语句集
              ResultSet rs = this.executeQuery(pstm, values)
        ) {

            // 4. 处理结果集
            ////5、关闭结果集
            //rs.close();
            ////6、关闭语句集
            //pstm.close();
            ////7、关闭连接
            //connection.close();
            return this.parseResultSet(rs, rowMapper);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    private ResultSet executeQuery(PreparedStatement pstm, Object[] values) throws SQLException {
        for (int i = 0; i < values.length; i++) {
            pstm.setObject(i, values[i]);
        }
        return pstm.executeQuery();
    }

    private List<?> parseResultSet(ResultSet rs, RowMapper<?> rowMapper) throws Exception {
        List<Object> result = new ArrayList<Object>();
        int rowNum = 0;
        while (rs.next()) {
            result.add(rowMapper.mapRow(rs, rowNum++));
        }
        return result;
    }


    private PreparedStatement createPrepareStatement(Connection conn, String sql) throws SQLException {
        return conn.prepareStatement(sql);
    }

    private Connection getConnection() throws SQLException {
        return this.dataSource.getConnection();
    }
}
